package org.meb.lotrlcg.db;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.meb.lotrlcg.db.dao.CardBaseDao;
import org.meb.lotrlcg.db.dao.CardDao;
import org.meb.lotrlcg.db.dao.CardSetBaseDao;
import org.meb.lotrlcg.db.dao.JpaDao;
import org.meb.lotrlcg.db.model.Card;
import org.meb.lotrlcg.db.model.CardBase;
import org.meb.lotrlcg.db.model.CardLang;
import org.meb.lotrlcg.db.model.CardSetBase;
import org.meb.lotrlcg.db.model.CycleBase;
import org.meb.lotrlcg.db.model.CycleLang;
import org.meb.lotrlcg.db.model.EncounterSet;
import org.meb.lotrlcg.db.model.EncounterSetBase;
import org.meb.lotrlcg.db.model.EncounterSetLang;
import org.meb.lotrlcg.db.model.ScenEnstLink;
import org.meb.lotrlcg.db.model.Scenario;
import org.meb.lotrlcg.db.model.ScenarioBase;
import org.meb.lotrlcg.db.model.ScenarioLang;
import org.meb.lotrlcg.db.util.DatabaseUtils;
import org.meb.lotrlcg.db.util.JpaUtils;
import org.meb.lotrlcg.db.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TmpLoader {

	private static final String BASE;
	static {
		String dropboxHome = System.getProperty("dropbox.home");
		if (StringUtils.isBlank(dropboxHome)) {
			throw new IllegalStateException("Dropbox home not set");
		}
		BASE = dropboxHome + "/workspace/lotrlcg/data/_raw/";
	}

	@SuppressWarnings("unused")
	private static final Logger log = LoggerFactory.getLogger(TmpLoader.class);

	private EntityManager em;
	private JpaDao<ScenarioBase> sbDao;
	private JpaDao<EncounterSetBase> esbDao;
	private JpaDao<CardSetBase> csbDao;
	private JpaDao<CardLang> clDao;
	private JpaDao<EncounterSet> esDao;
	private JpaDao<Scenario> sDao;

	public static void main(String[] args) throws IOException {
		TmpLoader loader = new TmpLoader();
		// loader.readPlUserCardsFromTextFile();
		// loader.readEncounterSetsFromTextFile();
//		loader.extractTraits();
		loader.writePlEncounterSetsAndScenarios();
//		loader.fillEncounterSetIds();
	}
	
	public TmpLoader() {
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("lotrlcgUnit");
		em = emf.createEntityManager();
		sbDao = new JpaDao<ScenarioBase>(em);
		esbDao = new JpaDao<EncounterSetBase>(em);
		csbDao = new JpaDao<CardSetBase>(em);
		clDao = new JpaDao<CardLang>(em);
		esDao = new JpaDao<EncounterSet>(em);
		sDao = new JpaDao<Scenario>(em);
	}

	private void writePlEncounterSetsAndScenarios() throws FileNotFoundException, IOException {
		em.getTransaction().begin();
		
		HashMap<String, String> map = new HashMap<String, String>();
		List<String> lines = IOUtils.readLines(new FileInputStream(BASE + "pl_crst_enst_scen.txt"));
		for (String line : lines) {
			if (StringUtils.isNotBlank(line)) {
				String[] tokens = line.split(";");
				if (tokens.length == 3) {
					map.put(tokens[1].trim(), tokens[2].trim());
					System.out.println(tokens[2].trim());
				}
			}
		}
		
		DatabaseUtils.executePrcSetUserLang(em, "pl");

		List<EncounterSet> encounterSets = esDao.find(new EncounterSet());
		for (EncounterSet encounterSet : encounterSets) {
			if (!encounterSet.isLocalized()) {
				if (map.containsKey(encounterSet.getTechName())) {
					encounterSet.setName(map.get(encounterSet.getTechName()));
					log.info("techName: {} , name: {}", encounterSet.getTechName(), map.get(encounterSet.getTechName()));
//					em.merge(encounterSet);
					em.flush();
				}
			}
		}
		
		List<Scenario> scenarios = sDao.find(new Scenario());
		for (Scenario scenario : scenarios) {
			if (!scenario.isLocalized()) {
				if (map.containsKey(scenario.getTechName())) {
					scenario.setName(map.get(scenario.getTechName()));
					log.info("techName: {} , name: {}", scenario.getTechName(), map.get(scenario.getTechName()));
//					em.merge(scenario);
					em.flush();
				}
			}
		}
		
		em.getTransaction().commit();
	}

	public void readCyclesFromTextFile() throws FileNotFoundException, IOException {
		em.getTransaction().begin();

		List<String> lines = IOUtils.readLines(new FileInputStream(
				"d:/dropbox/workspace/lotrlcg/scenario.txt"));
		boolean inCycle = false;
		CycleBase cycleBase = null;
		for (String line : lines) {
			if (StringUtils.isBlank(line) || line.startsWith("!")) {
				inCycle = false;
				cycleBase = null;
			} else if (line.startsWith("#")) {
				String cycleName = StringUtils.removeStart(line, "#").trim();
				String cycleTechName = Utils.toTechName(cycleName);
				String[] tokens = cycleName.split("\\s+", -1);
				String cycleCode = "";
				for (String token : tokens) {
					cycleCode += token.substring(0, 1);
				}
				cycleCode = cycleCode.toUpperCase();
				System.out.println("CYCLE: " + cycleName + " # " + cycleTechName + " # "
						+ cycleCode);

				inCycle = true;
				cycleBase = new CycleBase();
				cycleBase.setCode(cycleCode);
				cycleBase.setTechName(cycleTechName);
				CycleLang cycleLang = new CycleLang();
				cycleLang.setName(cycleName);
				cycleBase.getLangItems().put("en", cycleLang);
				JpaUtils.smartPersist(em, cycleBase);
			} else if (inCycle && line.startsWith("*")) {
				String[] tokens = StringUtils.removeStart(line, "*").trim().split(";", -1);
				String csName = tokens[0].trim();
				String csTechName = Utils.toTechName(tokens[0]).trim();
				System.out.println("SET:   " + csName + " # " + csTechName);

				CardSetBase csBase = new CardSetBase();
				csBase.setTechName(csTechName);
				csBase = new CardSetBaseDao(em).findUnique(csBase);
				if (csBase == null) {
					throw new RuntimeException("Unable to find card set base for tech name: "
							+ csTechName);
				}
				csBase.setCycleBase(cycleBase);
				cycleBase.getCardSetBaseItems().add(csBase);
				em.merge(cycleBase);
			}
		}

		em.flush();
		em.getTransaction().commit();
	}

	public List<ScenarioBase> readScenariosFromTextFile() throws FileNotFoundException, IOException {
		List<String> lines = IOUtils.readLines(new FileInputStream(
				"d:/dropbox/workspace/lotrlcg/scenario.txt"));

		ArrayList<ScenarioBase> scenarios = new ArrayList<ScenarioBase>();
		boolean inCycle = false;
		String csbName = null;
		int sequence = 1;

		for (String line : lines) {
			if (StringUtils.isBlank(line) || line.startsWith("--")) {
				continue;
			} else if (line.startsWith("#")) {
				inCycle = true;
				csbName = null;
				sequence = 1;
				continue;
			} else if (line.startsWith("!")) {
				inCycle = false;
				csbName = StringUtils.removeStart(line, "!").trim();
				sequence = 1;
				continue;
			}

			String[] tokens = StringUtils.removeStart(line, "*").trim().split(";", -1);
			String name = tokens[0].trim();
			Integer difficulty = null;
			if (StringUtils.isNotBlank(tokens[1])) {
				difficulty = Integer.valueOf(tokens[1].trim());
			}
			String description = StringEscapeUtils.unescapeJava(tokens[2]).trim();

			if (inCycle) {
				csbName = name;
			}
			String techName = Utils.toTechName(tokens[0]);
			String csbTechName = Utils.toTechName(csbName);
			System.out.println("SCENARIO: " + name + " -> " + techName);
			System.out.println("SET:      " + csbName + " -> " + csbTechName);

			ScenarioBase sb = new ScenarioBase();
			sb.setTechName(techName);
			sb.setDifficulty(difficulty);
			sb.setSequence(sequence++);
			ScenarioLang sl = new ScenarioLang();
			sl.setName(name);
			sl.setDescription(description);
			sb.getLangItems().put("en", sl);

			CardSetBase csb = new CardSetBase();
			csb.setTechName(csbTechName);
			csb = new CardSetBaseDao(em).findUnique(csb);
			if (csb == null) {
				throw new RuntimeException("Unable to find card set base for tech name: "
						+ csbTechName);
			}
			sb.setCardSetBase(csb);
			scenarios.add(sb);
		}

		return scenarios;
	}

	public List<EncounterSetBase> readEncounterSetsFromTextFile() throws FileNotFoundException,
			IOException {
		List<String> lines = IOUtils.readLines(new FileInputStream(BASE + "encounter.txt"));

		List<EncounterSetBase> scenarios = new ArrayList<EncounterSetBase>();

		int sequence = 1;
		HashMap<String, String> enstOwner = new HashMap<String, String>();
		ScenarioBase sb = null;

		em.getTransaction().begin();
		for (String line : lines) {
			if (StringUtils.isBlank(line) || line.startsWith("--")) {
				continue;
			} else if (line.startsWith("*")) {
				sequence = 1;
				String name = StringUtils.removeStart(line, "*").trim();
				sb = sbDao.findUnique(new ScenarioBase(Utils.toTechName(name)));
				continue;
			} else {
				String[] tokens = line.trim().split(";", -1);
				if (tokens.length != 2 || StringUtils.isBlank(tokens[0])
						|| StringUtils.isBlank(tokens[1])) {
					throw new IllegalStateException("Blank token in line: " + line);
				}

				String enstName = tokens[0].trim();
				String enstTechName = Utils.toTechName(enstName);
				String crstName = tokens[1].trim();
				String crstTechName = Utils.toTechName(crstName);
				EncounterSetBase esb = esbDao.findUnique(new EncounterSetBase(enstTechName));
				boolean persist = false;
				if (esb == null) {
					esb = new EncounterSetBase(enstTechName);
					CardSetBase csb = csbDao.findUnique(new CardSetBase(crstTechName));
					esb.setCardSetBase(csb);
					EncounterSetLang esl = new EncounterSetLang();
					esl.setName(enstName);
					esl.setBase(esb);
					esl.setLangCode("en");
					esb.getLangItems().put("en", esl);

					// em.persist(esb);
					// em.flush();
					persist = true;
				} else {
					// CardSetBase csb = csbDao.findUnique(new
					// CardSetBase(crstTechName));
					// esb.setCardSetBase(csb);
					// em.merge(esb);
					// em.flush();
				}

				ScenEnstLink scenEnstLink = new ScenEnstLink();
				scenEnstLink.setScenarioBase(sb);
				scenEnstLink.setEncounterSetBase(esb);
				scenEnstLink.setSequence(sequence++);
				esb.getScenEnstLinkItems().add(scenEnstLink);
				System.out.println(sb);
				System.out.println(line);

				System.out.println(crstName + " # " + enstName + " # " + sb.getTechName());
				if (persist) {
					em.persist(esb);
				} else {
					em.merge(esb);
				}
				em.flush();

				if (enstOwner.containsKey(tokens[0]) && !enstOwner.get(tokens[0]).equals(tokens[1])) {
					throw new IllegalStateException("Invalid assignment: " + line);
				}
				enstOwner.put(tokens[0].trim(), tokens[1].trim());
			}

		}

		em.getTransaction().commit();

		return scenarios;
	}

	public List<CardBase> readPlUserCardsFromTextFile() throws FileNotFoundException, IOException {
		List<String> lines = IOUtils.readLines(new FileInputStream(BASE + "event.txt"));

		StringBuilder ok = new StringBuilder();
		StringBuilder error = new StringBuilder();

		em.getTransaction().begin();

		Pattern pattern = Pattern.compile("[^#]+#([0-9]+).*");
		List<CardBase> cards = new ArrayList<CardBase>();
		for (String line : lines) {
			try {
				if (StringUtils.isBlank(line) || line.startsWith("#")) {
					continue;
				}

				String[] tokens = line.split(";", -1);
				String name = StringUtils.trimToNull(tokens[0]);
				String[] attrs = new String[6];

				// int offset = 4;
				//
				// // HERO
				// attrs[0] = StringUtils.trimToNull(tokens[7 - offset]);
				// attrs[1] = StringUtils.trimToNull(tokens[8 - offset]);
				// attrs[2] = StringUtils.trimToNull(tokens[9 - offset]);
				// attrs[3] = StringUtils.trimToNull(tokens[10 - offset]);
				// attrs[4] = StringUtils.trimToNull(tokens[11 - offset]);
				// attrs[5] = StringUtils.trimToNull(tokens[12 - offset]);
				// String text = StringUtils.trimToNull(tokens[13 - offset]);
				// String setAndNumber = StringUtils.trimToNull(tokens[14 -
				// offset]);
				// String setTechName = StringUtils.trimToNull(tokens[15 -
				// offset]);
				//
				// // ALLY/ATTACHMENT
				// String unique = StringUtils.trimToNull(tokens[17 - offset]);

				// EVENT
				attrs[0] = StringUtils.trimToNull(tokens[3]);
				attrs[3] = StringUtils.trimToNull(tokens[4]);
				String text = StringUtils.trimToNull(tokens[5]);
				String setAndNumber = StringUtils.trimToNull(tokens[6]);
				String setTechName = StringUtils.trimToNull(tokens[7]);

				Integer number = null;
				Matcher matcher = pattern.matcher(setAndNumber);
				if (matcher.matches()) {
					number = Integer.valueOf(matcher.group(1));
				}

				Card card = new Card();
				card.setCrstTechName(setTechName);
				card.setNumber(number);
				card = new CardDao(em).findUnique(card);

				if (card == null) {
					error.append(name + "\t" + setAndNumber + "\t" + setTechName).append("\n");
				} else {
					ok.append(card.getId() + "\t" + name + "\t" + setAndNumber + "\t" + setTechName)
							.append("\n");

					String langCode = "pl";
					CardBase cb = new CardBaseDao(em).findUnique(card.getId());
					// cb.setUnique("T".equals(unique));
					CardLang cl = cb.getLangItems().get(langCode);
					if (cl == null) {
						cl = new CardLang();
						cl.setBase(cb);
						cb.getLangItems().put(langCode, cl);
						cl.setLangCode(langCode);
						cl.setName(name);
						cl.setText(text);

						StringBuilder builder = new StringBuilder();
						for (int i = 0; i < 3; i++) {
							if (StringUtils.isNotBlank(attrs[i])) {
								builder.append(attrs[i].trim()).append(". ");
							}
						}
						cl.setTrait(StringUtils.trimToNull(StringUtils.chomp(builder.toString())));

						builder = new StringBuilder();
						for (int i = 3; i < 6; i++) {
							if (StringUtils.isNotBlank(attrs[i])) {
								builder.append(attrs[i].trim()).append(". ");
							}
						}
						cl.setAbility(StringUtils.trimToNull(StringUtils.chomp(builder.toString())));
						em.persist(cl);
						em.flush();
					}
				}
			} catch (Exception e) {
				error.append(e.getMessage()).append(": ").append(line).append("\n");
			}
		}

		System.out.println(ok.toString());
		System.err.println(error.toString());

		if (error.length() > 0) {
			em.getTransaction().rollback();
		} else {
			em.getTransaction().commit();
		}
		return cards;
	}

	public void extractTraits() {
		Set<String> traits = new TreeSet<String>();
		List<CardBase> cards = new DataLoader().readCardsFromDatabase();
		
		StringBuilder builder = new StringBuilder();
		
		for (CardBase base : cards) {
			CardLang en = base.getLangItems().get("en");
			CardLang de = base.getLangItems().get("de");
			CardLang pl = base.getLangItems().get("pl");
			
			String[] enTraitArray = createTraitArray(en, builder);
			String[] deTraitArray = createTraitArray(de, builder);
			String[] plTraitArray = createTraitArray(pl, builder);
			builder.append("\n---\n");

			for (int i = 0; i < 3; i++) {
				traits.add(enTraitArray[i] + " " + deTraitArray[i] + " " + plTraitArray[i]);
			}
		}
		
		System.out.println(builder.toString());

		for (String trait : traits) {
			System.out.println(trait);
		}
	}

	private String[] createTraitArray(CardLang lang, StringBuilder builder) {
		String[] traitArray = new String[] { "", "", "" };
		if (lang != null && lang.getTrait() != null) {
			String trait = lang.getTrait();
			builder.append(trait).append("\n");
			String[] tmp = trait.split("\\.");
			for (int i = 0; i < tmp.length; i++) {
				if (tmp[i] != null) {
					traitArray[i] = tmp[i].trim() + ".";
				}
			}
		}
		return traitArray;
	}
	
	public void fillEncounterSetIds() {
		em.getTransaction().begin();
		List<CardBase> cards = new DataLoader().readCardsFromDatabase();
		for (CardBase base : cards) {
			CardLang lang = base.getLangItems().get("en");
			if (lang == null || lang.getEncounterSet() == null) {
				continue;
			}
			String enstTechName = Utils.toTechName(lang.getEncounterSet());
			log.info("encounter set name: {}, tech name: {}", lang.getEncounterSet(), enstTechName);
			EncounterSetBase esb = esbDao.findUnique(new EncounterSetBase(enstTechName));
			if (esb == null) {
				throw new IllegalStateException("Not found");
			}
			base.setEncounterSetBase(esb);
			em.merge(base);
			em.flush();
		}
		
		em.getTransaction().commit();
	}
}
